package com.hornyun.opc.client;

import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.*;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned;
import org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateRequest;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoringParameters;
import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
import org.slf4j.LoggerFactory;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;

import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;

/**
 * @author hornyun
 * @date 2020/11/24 9:54 AM
 */

public class ClientMain {

    OpcUaClient opcUaClient;

    public ClientMain() throws Exception {
        Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            throw new Exception("unable to create security dir: " + securityTempDir);
        }

        LoggerFactory.getLogger(ClientMain.class)
                .info("security temp dir: {}", securityTempDir.toAbsolutePath());

        KeyStoreLoader loader = new KeyStoreLoader().load(securityTempDir);

        opcUaClient = OpcUaClient.create(
                "opc.tcp://192.168.7.130:49320",
                endpoints ->
                        endpoints.stream()
                                .filter(e -> true)
                                .findFirst(),
                configBuilder ->
                        configBuilder
                                .setApplicationName(LocalizedText.english("eclipse milo opc-ua client"))
                                .setApplicationUri("urn:eclipse:milo:examples:client")
                                .setCertificate(loader.getClientCertificate())
                                .setKeyPair(loader.getClientKeyPair())
                                .setIdentityProvider(new AnonymousProvider())
                                .setRequestTimeout(uint(5000))
                                .build()
        );
    }



    public static void main(String[] args) throws Exception {
        ClientMain clientMain = new ClientMain();

        String node1 = "ns=2;s=channel2.eqp1.group1.k1";
        String node2 = "ns=2;s=channel2.eqp1.group1.k2";
//        writeValue(clientMain.opcUaClient, node1, 13);
//
//        Object o = readValue(clientMain.opcUaClient, node1);
//        System.out.println("read value is " + o);

        createSubscription(clientMain.opcUaClient, node1);
        while (true) {
            Thread.sleep(2000);

        }
    }

    public static Object readValue(OpcUaClient opcUaClient, String nodeStr) throws Exception{
        NodeId nodeId = NodeId.parse(nodeStr);
        opcUaClient.connect().get();

        DataValue value = opcUaClient.readValue(0.0, TimestampsToReturn.Neither, nodeId).get();
        Optional<ExpandedNodeId> dataType = value.getValue().getDataType();
        System.out.println("read data type is  " + dataType);
        opcUaClient.disconnect().get();
        return value.getValue().getValue();
    }

    public static void createSubscription(OpcUaClient opcUaClient,String nodeStr) throws ExecutionException, InterruptedException {
        opcUaClient.connect().get();
        UaSubscription subscription = opcUaClient.getSubscriptionManager().createSubscription(1000D).get();
        NodeId nodeId = NodeId.parse(nodeStr);
        ReadValueId readValueId = new ReadValueId(nodeId, AttributeId.Value.uid(), null, null);

        MonitoringParameters parameters = new MonitoringParameters(uint(1), 1000D, null, uint(10), true);

        MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(readValueId, MonitoringMode.Reporting, parameters);

        List<MonitoredItemCreateRequest> requests = Collections.singletonList(request);

        List<UaMonitoredItem> items = subscription.createMonitoredItems(
                TimestampsToReturn.Neither,
                requests,
                (item,id)->{
                    item.setValueConsumer((item1, value)->{
                        System.out.println("nodeid :"+item1.getReadValueId().getNodeId());
                        System.out.println("value :"+value.getValue().getValue());
                    });
                }
        ).get();

    }

    public static void writeValue(OpcUaClient opcUaClient, String nodeStr,Object value) throws Exception {

        opcUaClient.connect().get();
        NodeId nodeId = NodeId.parse(nodeStr);



        Variant v = new Variant(Unsigned.ushort(22));

        Optional<ExpandedNodeId> dataType = v.getDataType();
        System.out.println("write data type is " + dataType);
        DataValue dataValue = new DataValue(v, null, null);


        StatusCode statusCode = opcUaClient.writeValue(nodeId, dataValue).get();

        System.out.println("write value " + statusCode.isGood());
    }


}
